Skip to content

aya: Support loading programs with ksyms#1372

Open
altugbozkurt07 wants to merge 1 commit intoaya-rs:mainfrom
altugbozkurt07:ksysm
Open

aya: Support loading programs with ksyms#1372
altugbozkurt07 wants to merge 1 commit intoaya-rs:mainfrom
altugbozkurt07:ksysm

Conversation

@altugbozkurt07
Copy link

@altugbozkurt07 altugbozkurt07 commented Oct 22, 2025

Hi,

this commit covers the first iteration of supporting programs with ksyms variables in aya. I tried to replicate the functionality based on this commit. So to wrap it up;

BTF Parsing and Collection (aya-obj/src/obj.rs):

Added collect_ksyms_from_btf() to parse .ksyms DATASEC from BTF
Implemented ExternCollection to store extern symbol metadata
Created dummy variable handling for function externs in DATASEC
Added BTF fixup logic via fixup_ksyms_datasec() to make externs kernel-acceptable

Extern Type System (aya-obj/src/extern_types.rs):

Implemented ExternDesc to track extern functions and variables
Added resolve_extern_ksyms() for resolving externs against kernel BTF
Created separate resolution paths for functions (resolve_extern_function_internal) and variables (resolve_extern_variable_internal)
Added type compatibility checking using types_are_compatible()
For variables, if they cant be resolved against target kernel btf, resolution through kallsyms is implemented as a fallback

Relocation Support (aya-obj/src/relocation.rs):

Added relocate_externs() to patch LD_IMM64 and CALL instructions with resolved kernel BTF IDs
Implemented instruction patching for both function calls and variable references.

Integration (aya/src/bpf.rs):

Integrated extern resolution into the EbpfLoader flow
Added kernel BTF loading and extern resolution before program loading

What we are missing:

1- We need more integration tests to cover different use-cases to make sure everything is properly implemented.
Some of the tests that needs to be covered are:

  • Weak symbols tests
  • Extern variable tests
  • Error handlings tests(non-existent extern symbol, function with wrong signature, etc)
  • BTF fixup tests

2- We are missing externs in kernel modules, this is something that is supported by libbpf, at this point it should not be that hard to implement it but before that i would like to first make sure the changes i introduce to aya aligns with the aya infra structure in terms of API desing.

3- Kconfigs are also missing, but again once this iteration goes through, it would be easier to extend this to cover Kconfigs as well.

I would really appreciate your feedback on this and let me know if i am missing anything else, or my implementation is not properly covering the described use-cases.


This change is Reviewable

@netlify
Copy link

netlify bot commented Oct 22, 2025

Deploy Preview for aya-rs-docs ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit fb57c29
🔍 Latest deploy log https://app.netlify.com/projects/aya-rs-docs/deploys/697ca139bac3fb0008d799a0
😎 Deploy Preview https://deploy-preview-1372--aya-rs-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@mergify mergify bot added aya This is about aya (userspace) aya-obj Relating to the aya-obj crate test A PR that improves test cases or CI labels Oct 22, 2025
Copy link
Member

@vadorovsky vadorovsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this and kudos for making it work!

The major issue causing the CI failures is usage of std which needs to be guarded.

I still need to play with the code, but I left some nitpicks about dosctrings and comments.

Reviewable status: 0 of 11 files reviewed, 25 unresolved discussions


aya-obj/src/lib.rs line 79 at r1 (raw file):

pub mod btf;
/// ksym implementation

Let's remove this. If you want to have a top-level documentation of the module, add it in the top of the module file with !//.


aya-obj/src/extern_types.rs line 1 at r1 (raw file):

use std::{

This is the reason why CI is failing. The aya-obj crate supports no_std, so everything requiring std needs to be guarded with #[cfg(feature = "std")]. See the other modules

I still need to take a deeper look, but given that this whole ksym detection/sanitization mechanism clearly requires allocations and string operations, we could probably just guard the whole module with #[cfg(feature = "std")].

Another option would be trying to make it no_std, but I don't think it's possible.


aya-obj/src/extern_types.rs line 58 at r1 (raw file):

            }

            // Dispatch to appropriate resolver

I would remove all the comments in this function except that one.


aya-obj/src/extern_types.rs line 99 at r1 (raw file):

    fn resolve_kallsyms(&mut self) -> std::result::Result<(), KsymResolveError> {
        use std::{

Move the imports up.


aya-obj/src/extern_types.rs line 122 at r1 (raw file):

        let file =
            File::open("/proc/kallsyms").map_err(|e| KsymResolveError::KallsymsReadError {
                error: e.to_string(),

We shouldn't include other errors with string.thiserror can wrap other error types inside your own one.


aya-obj/src/extern_types.rs line 227 at r1 (raw file):

    /// Internal: Resolve a single extern variable
    /// Returns Some(btf_id) on success, None for weak externs not found

Code snippet:

/// Resolves a single exterm variable. Returns BTF ID if found, otherwise
/// returns `None`.

aya/src/bpf.rs line 97 at r1 (raw file):

}

pub(crate) static KERNEL_BTF: LazyLock<Option<Btf>> = LazyLock::new(|| match Btf::from_sys_fs() {

btw, I still need to play with the code myself, but I don't like having it as a global variable. If it's really difficult to pass it across functions, we could make it a part of some struct and write methods.


aya/src/bpf.rs line 1195 at r1 (raw file):

    /// Error resolving extern kernel symbols (functions and variables)
    #[error("kernel symbol resolution error: {0}")]
    KsymResolve(#[from] aya_obj::KsymResolveError),

Usually we are trying to keep the error variants in EbpfError very general, and treat each variant as a category of errors. Given that these two are about ksyms, I would try to pack them in one error type first (let's stay, KsymError), then include it here.


test/integration-test/bpf/ksysm.bpf.c line 18 at r1 (raw file):

extern void bpf_rcu_read_lock(void) __attribute__((section(".ksyms")));
extern void bpf_rcu_read_unlock(void) __attribute__((section(".ksyms")));

Would be nice to add a similar Rust eBPF program.


aya-obj/src/btf/btf.rs line 819 at r1 (raw file):

    /// This modifies extern functions and variables to make them acceptable to the kernel:
    /// - Functions: Change linkage to GLOBAL, fix param names, replace with dummy var in datasec
    /// - Variables: Change linkage to GLOBAL_ALLOCATED, replace type with int

Code snippet:

/// Fixes up BTF for `.ksyms` datasec entries containing extern kernel symbol and
/// makes it acceptable by the kernel:
///
/// * Changes linkage of extern functions to `GLOBAL`, fixes parameter names, injects
///   a dummy variable representing them in datasec.
/// * Changes linkage of extern variables to `GLOBAL_ALLOCATED`, replaces their type
///   with `int`.

aya-obj/src/btf/btf.rs line 825 at r1 (raw file):

        dummy_var_id: Option<u32>,
    ) -> Result<(), BtfError> {
        // Get dummy var info and int type ID upfront

I would try to make this comment more descriptive (for the whole let ... else block`) and remove all the others.


aya-obj/src/relocation.rs line 184 at r1 (raw file):

    pub fn relocate_externs(&mut self) -> Result<(), EbpfRelocationError> {
        for (name, extern_desc) in &self.externs.externs {
            eprintln!(

Use debug!, instead of eprintln!


aya-obj/src/relocation.rs line 246 at r1 (raw file):

fn relocate_externs<'a, I: Iterator<Item = &'a Relocation>>(
    fun: &mut Function, // ← Different first parameter (not &mut self)

Remove this comment, it brings no value. It's not even a method, so why would it have self?


aya-obj/src/obj.rs line 872 at r1 (raw file):

        };

        info!("Found/created int type_id: {}", int_type_id);

I would make it debug! and print it only if we create the int type.


aya-obj/src/obj.rs line 890 at r1 (raw file):

        // After creating dummy_var
        info!("Created dummy_var type_id: {}", dummy_var_id);

debug!


aya-obj/src/obj.rs line 898 at r1 (raw file):

    }

    /// Collect extern kernel symbols from BTF DATASEC entries

Function/method documentation should be written using the 3rd person, so starting from "Collects". And if you use full sentences, finish them with ..

Let's also be consistent with capitalizing (or not capitalizing) names. We either write "DATASEC" or "datasec" everywhere. I'm leaning towards the latter, since it's just a convenient shortened form of "data section", not really a proper name.

Code snippet:

/// Collects extern kernel symbols from BTF datasec entries.

aya-obj/src/obj.rs line 900 at r1 (raw file):

    /// Collect extern kernel symbols from BTF DATASEC entries
    fn collect_ksyms_from_btf(&mut self) -> Result<(), ParseError> {
        // Find .ksyms DATASEC

Remove all these comments, the code is self-explanatory enough.


aya-obj/src/obj.rs line 927 at r1 (raw file):

    }

    /// Find .ksyms DATASEC in BTF, returns (id, datasec) if found

Code snippet:

/// Searches for the `.ksyms` datasec in BTF, returns it if found.

aya-obj/src/obj.rs line 945 at r1 (raw file):

    }

    /// Check if datasec contains any functions

Code snippet:

/// Checks if datasec dontains any functions.

aya-obj/src/obj.rs line 954 at r1 (raw file):

    }

    /// Collect extern descriptors from datasec entries

Code snippet:

/// Collects extern descriptors from datasec entries.

aya-obj/src/obj.rs line 961 at r1 (raw file):

        for entry in &datasec.entries {
            let Some(extern_desc) = self.process_datasec_entry(btf, entry)? else {
                continue; // Skip non-extern entries

Remove this comment.


aya-obj/src/obj.rs line 970 at r1 (raw file):

    }

    /// Process a single datasec entry, returning ExternDesc if it's an extern

Aside from what I mentioned earlier, try to use ``` for type names. If the types are inside the same crate, you can even link them like:

Code snippet:

/// Processes a single datasec entry, returns [`ExternDesc`] if it's an extern.

aya-obj/src/obj.rs line 978 at r1 (raw file):

        let btf_type = btf.type_by_id(entry.btf_type)?;

        // Extract extern info based on type

Remove these overly explicit comments.


aya-obj/src/obj.rs line 1017 at r1 (raw file):

    }

    /// Apply BTF fixups for .ksyms DATASEC

Code snippet:

/// Applies BTF fixups for `.ksyms` datasec.

aya-obj/src/obj.rs line 1026 at r1 (raw file):

    }

    /// Helper to find symbol by name

Don't start function/method documentation with "Helper", use the 3rd person and explain what it does.

Code snippet:

/// Return a symbol with the given `name` from the symbol table.

@altugbozkurt07 altugbozkurt07 force-pushed the ksysm branch 4 times, most recently from 01a0eec to 4cd24e4 Compare October 29, 2025 14:56
Copy link
Member

@vadorovsky vadorovsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more comments about code organization, the kind of comments you were asking for. 😄

Copy link
Author

@altugbozkurt07 altugbozkurt07 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 15 files reviewed, 36 unresolved discussions (waiting on @vadorovsky)


aya/src/bpf.rs line 97 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

btw, I still need to play with the code myself, but I don't like having it as a global variable. If it's really difficult to pass it across functions, we could make it a part of some struct and write methods.

removed, now we are using already parsed kernel btf.


aya/src/bpf.rs line 1195 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Usually we are trying to keep the error variants in EbpfError very general, and treat each variant as a category of errors. Given that these two are about ksyms, I would try to pack them in one error type first (let's stay, KsymError), then include it here.

Done.


aya-obj/src/extern_types.rs line 1 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

This is the reason why CI is failing. The aya-obj crate supports no_std, so everything requiring std needs to be guarded with #[cfg(feature = "std")]. See the other modules

I still need to take a deeper look, but given that this whole ksym detection/sanitization mechanism clearly requires allocations and string operations, we could probably just guard the whole module with #[cfg(feature = "std")].

Another option would be trying to make it no_std, but I don't think it's possible.

Done.


aya-obj/src/extern_types.rs line 58 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

I would remove all the comments in this function except that one.

Done.


aya-obj/src/extern_types.rs line 99 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Move the imports up.

would not it be better to be stayed in the function scope since this is only enabled with std feature


aya-obj/src/extern_types.rs line 122 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

We shouldn't include other errors with string.thiserror can wrap other error types inside your own one.

Done.


aya-obj/src/lib.rs line 79 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Let's remove this. If you want to have a top-level documentation of the module, add it in the top of the module file with !//.

Done.


aya-obj/src/obj.rs line 872 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

I would make it debug! and print it only if we create the int type.

Done.


aya-obj/src/obj.rs line 890 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

debug!

Done.


aya-obj/src/obj.rs line 898 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Function/method documentation should be written using the 3rd person, so starting from "Collects". And if you use full sentences, finish them with ..

Let's also be consistent with capitalizing (or not capitalizing) names. We either write "DATASEC" or "datasec" everywhere. I'm leaning towards the latter, since it's just a convenient shortened form of "data section", not really a proper name.

Done.


aya-obj/src/obj.rs line 900 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Remove all these comments, the code is self-explanatory enough.

Done.


aya-obj/src/obj.rs line 961 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Remove this comment.

Done.


aya-obj/src/obj.rs line 970 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Aside from what I mentioned earlier, try to use ``` for type names. If the types are inside the same crate, you can even link them like:

Done.


aya-obj/src/obj.rs line 978 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Remove these overly explicit comments.

Done.


aya-obj/src/obj.rs line 1026 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Don't start function/method documentation with "Helper", use the 3rd person and explain what it does.

Done.


aya-obj/src/relocation.rs line 184 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Use debug!, instead of eprintln!

Done.


aya-obj/src/relocation.rs line 246 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Remove this comment, it brings no value. It's not even a method, so why would it have self?

Done.


aya-obj/src/btf/btf.rs line 825 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

I would try to make this comment more descriptive (for the whole let ... else block`) and remove all the others.

Done.


test/integration-test/bpf/ksysm.bpf.c line 18 at r1 (raw file):

Previously, vadorovsky (Michal R) wrote…

Would be nice to add a similar Rust eBPF program.

I added a similar rust program, but extern definitions debug information is not emitted by rustc so that the proper btf is generated by llvm. We need a thread in discord regarding this.


aya-obj/src/extern_types.rs line 227 at r1 (raw file):

    /// Internal: Resolve a single extern variable
    /// Returns Some(btf_id) on success, None for weak externs not found

Done.


aya-obj/src/btf/btf.rs line 819 at r1 (raw file):

    /// This modifies extern functions and variables to make them acceptable to the kernel:
    /// - Functions: Change linkage to GLOBAL, fix param names, replace with dummy var in datasec
    /// - Variables: Change linkage to GLOBAL_ALLOCATED, replace type with int

Done.


aya-obj/src/obj.rs line 927 at r1 (raw file):

    }

    /// Find .ksyms DATASEC in BTF, returns (id, datasec) if found

Done.


aya-obj/src/obj.rs line 945 at r1 (raw file):

    }

    /// Check if datasec contains any functions

Done.


aya-obj/src/obj.rs line 954 at r1 (raw file):

    }

    /// Collect extern descriptors from datasec entries

Done.


aya-obj/src/obj.rs line 1017 at r1 (raw file):

    }

    /// Apply BTF fixups for .ksyms DATASEC

Done.


bpf_rcu_read_unlock();
return 0;
} No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a new line.

Comment on lines +1 to +4
#include <vmlinux.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
Copy link
Member

@vadorovsky vadorovsky Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format is complaining about the order of these imports. But vmlinux.h has to be first, since all the BPF headers depend on it.

In all other .c files we work around this issue by disabling clang-format for includes:

Suggested change
#include <vmlinux.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
// clang-format off
#include <vmlinux.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
// clang-format on

@altugbozkurt07 altugbozkurt07 force-pushed the ksysm branch 2 times, most recently from e9d8861 to 171d8a6 Compare January 4, 2026 00:39
@altugbozkurt07
Copy link
Author

@vadorovsky , hi there i extended the test cases to cover the core use cases. So this should provide better consistency and confidence in overall ksyms implementation in this PR, and thankfully all tests are passing finally :). I would appreciate a review on the latest changes and lets see if there is anything else needed before merging this.

@altugbozkurt07 altugbozkurt07 force-pushed the ksysm branch 2 times, most recently from d359a6e to 32b4cf4 Compare January 19, 2026 16:05
Comment on lines 84 to 104
// // Strong typed ksyms require the symbol to be in kallsyms.
// // With CONFIG_KALLSYMS_ALL=n, only function symbols are exported,
// // not variables like bpf_prog_active. The verifier uses
// // bpf_kallsyms_lookup_name() internally and will reject the program
// // if the symbol address cannot be resolved.
// if !kallsyms_available() {
// eprintln!("skipping test, kallsyms not available");
// return;
// }
// match kallsyms_find("bpf_prog_active") {
// Some(addr) if addr != 0 => {
// eprintln!("bpf_prog_active found in kallsyms at {:#x}", addr);
// }
// _ => {
// eprintln!(
// "skipping test: bpf_prog_active not in kallsyms \
// (CONFIG_KALLSYMS_ALL=n? only function symbols exported)"
// );
// return;
// }
// }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check looks in principle good to me. I was initially thinking about parsing the kernel config, but I like the solution of looking for the kallsym better. Let's just polish it up a bit. there is no need to print stuff in case of success.

Also, I think we should get rid of the addr != 0 check. Did you see that actually happening (address being Some(0))? If so, we need to understand why - and also try to make sure that the broken case always returns None. If not, then let's just remove the check.

Suggested change
// // Strong typed ksyms require the symbol to be in kallsyms.
// // With CONFIG_KALLSYMS_ALL=n, only function symbols are exported,
// // not variables like bpf_prog_active. The verifier uses
// // bpf_kallsyms_lookup_name() internally and will reject the program
// // if the symbol address cannot be resolved.
// if !kallsyms_available() {
// eprintln!("skipping test, kallsyms not available");
// return;
// }
// match kallsyms_find("bpf_prog_active") {
// Some(addr) if addr != 0 => {
// eprintln!("bpf_prog_active found in kallsyms at {:#x}", addr);
// }
// _ => {
// eprintln!(
// "skipping test: bpf_prog_active not in kallsyms \
// (CONFIG_KALLSYMS_ALL=n? only function symbols exported)"
// );
// return;
// }
// }
// Strong typed ksyms require the symbol to be in kallsyms.
// With CONFIG_KALLSYMS_ALL=n, only function symbols are exported,
// not variables like bpf_prog_active. The verifier uses
// bpf_kallsyms_lookup_name() internally and will reject the program
// if the symbol address cannot be resolved.
if !kallsyms_available() {
eprintln!("skipping test, kallsyms not available");
return;
}
if kallsyms_find("bpf_prog_active").is_none() {
eprintln!(
"skipping test, bpf_prog_active kallsyms not available
(usually due to CONFIG_KALLSYMS_ALL kernel configuration disabled)"
);
return;
}


use anyhow::Context as _;

/// Dump kernel configuration and kallsyms info relevant to ksym tests.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was definitely useful for your local debugging, but given that you've found the solution, can we remove this?

Comment on lines 11 to 35
fn kallsyms_available() -> bool {
File::open("/proc/kallsyms")
.ok()
.map(|f| {
BufReader::new(f)
.lines()
.take(100)
.filter_map(|l| l.ok())
.filter_map(|l| l.split_whitespace().next().map(String::from))
.filter_map(|s| u64::from_str_radix(&s, 16).ok())
.any(|addr| addr != 0)
})
.unwrap_or(false)
}

fn kallsyms_find(symbol_name: &str) -> Option<u64> {
let file = File::open("/proc/kallsyms").ok()?;
for line in BufReader::new(file).lines().filter_map(|l| l.ok()) {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 3 && parts[2] == symbol_name {
return u64::from_str_radix(parts[0], 16).ok();
}
}
None
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of take(100)? I don't think it's correct, there is no guarantee that the kallsyms we look for are in the first N of entries. Reading the whole file makes the test execution slightly longer, but it's still nothing noticeable.

Ignoring errors with .ok() is not great for debugging, in case something goes wrong. Imagine if the format of kallsyms changes some day, or if the current parsing mechanism does not always work with all configurations. With your current code, it will just throw None and one will have to spend time debugging. If we threw errors instead, it would be more clear which line failed. If we add some context to the errors, we can make the reason behind the failure crystal clear.
In tests, we are panicking instead of using anyhow, so we either:

  • Use .unwrap() or expect(). But the downside of .unwrap() is that we can't add more context. The downside of expect() is that it's being evaluated even if the error is not hit, which is fine if you pass a simple string (e.g. .expect("my message")), but is not fine once you are formatting arguments (e.g. .expect(format!("failed to read from file {file}"))), because then the format! call will be triggered unconditionally.
  • My preference - the following unwrap_or_else(|e| panic!("message: {e:?}")) pattern (so the formatting happens only if we actually hit the error) to add context to the errors before panicking:

netlink_set_link_up(idx as i32)
.unwrap_or_else(|e| panic!("failed to set `lo` up in netns {}: {e}", ns.name));

Ignoring errors that are OK to happen (due to the kernel not supporting a certain thing etc.) is fine, but from what I see, a failure in any of your .ok() calls you're issuing would mean a bug in your parsing logic. With a kernel with no kallsyms, we should be still able to parse stuff until we hit addr != 0.

Your attempt to use lazy iterators and filter_map chains is noble, but I think it's not great for emitting errors, so I wouldn't mind a regular loop.

Also, the parts of the kallsyms entry could be parsed by using slice pattern matching instead of soing the manual if parts.len() check. You can replace:

        let parts: Vec<&str> = line.split_whitespace().collect();
        if parts.len() >= 3 && parts[2] == symbol_name {
            [...]
        }

with:

        if let [addr, _type, name, ..] = parts.as_slice()
            && *name == symbol_name
        {
            [...]
        }

To sum it up, my proposed change:

Suggested change
fn kallsyms_available() -> bool {
File::open("/proc/kallsyms")
.ok()
.map(|f| {
BufReader::new(f)
.lines()
.take(100)
.filter_map(|l| l.ok())
.filter_map(|l| l.split_whitespace().next().map(String::from))
.filter_map(|s| u64::from_str_radix(&s, 16).ok())
.any(|addr| addr != 0)
})
.unwrap_or(false)
}
fn kallsyms_find(symbol_name: &str) -> Option<u64> {
let file = File::open("/proc/kallsyms").ok()?;
for line in BufReader::new(file).lines().filter_map(|l| l.ok()) {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 3 && parts[2] == symbol_name {
return u64::from_str_radix(parts[0], 16).ok();
}
}
None
}
const PROC_KALLSYMS: &str = "/proc/kallsyms";
fn kallsyms_available() -> bool {
let file = File::open(PROC_KALLSYMS)
.unwrap_or_else(|e| panic!("failed to open {PROC_KALLSYMS}: {e:?}"));
for line in BufReader::new(file).lines() {
let line =
line.unwrap_or_else(|e| panic!("failed to read the line from {PROC_KALLSYMS}: {e:?}"));
if let Some(addr) = line.split_whitespace().next() {
let addr = u64::from_str_radix(addr, 16)
.unwrap_or_else(|e| panic!("failed to parse the address: {addr}: {e:?}"));
if addr != 0 {
return true;
}
}
}
return false;
}
fn kallsyms_find(symbol_name: &str) -> Option<u64> {
let file = File::open(PROC_KALLSYMS)
.unwrap_or_else(|e| panic!("failed to open {PROC_KALLSYMS}: {e:?}"));
for line in BufReader::new(file).lines() {
let line =
line.unwrap_or_else(|e| panic!("failed to read the line from {PROC_KALLSYMS}: {e:?}"));
let parts: Vec<&str> = line.split_whitespace().collect();
if let [addr, _type, name, ..] = parts.as_slice()
&& *name == symbol_name
{
let addr = u64::from_str_radix(addr, 16)
.unwrap_or_else(|e| panic!("failed to parse the address: {addr}: {e:?}"));
return Some(addr);
}
}
None
}

Comment on lines 41 to 44
let btf = match Btf::from_sys_fs() {
Ok(b) => b,
Err(_) => return false,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other tests have a hard unwrap on Btf::from_sys_fs. I think it's fine if you just unwrap or expect:

Besides, I think the Btf::from_sys_fs should be removed in this function and instead you should pass it as an argument, so we don't parse BTF multiple times. See the comment below.

.try_into()
.unwrap();

let btf = Btf::from_sys_fs().unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could move this up before if !btf_has_percpu_datasec() and make the tf_has_percpu_datasec function take &Btf, so we don't call Btf::from_sys_fs() multiple times.

Comment on lines 115 to 117
if let Err(e) = prog.load("sys_enter", &btf) {
panic!("load error: {e:?}");
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are adding context to the error (nice), let's improve the message?

Suggested change
if let Err(e) = prog.load("sys_enter", &btf) {
panic!("load error: {e:?}");
}
const SYS_ENTER: &str = "sys_enter";
if let Err(e) = prog.load(SYS_ENTER, &btf) {
panic!("failed to load program {SYS_ENTER}: {e:?}");
}

.unwrap();
assert!(
per_cpu_addr != 0,
"bpf_per_cpu_ptr address should be non-zero"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the descriptive messages in asserts. 👍

@altugbozkurt07 altugbozkurt07 force-pushed the ksysm branch 4 times, most recently from ef42763 to 17151ae Compare January 29, 2026 08:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aya This is about aya (userspace) aya-obj Relating to the aya-obj crate test A PR that improves test cases or CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants